/********************************************************************************
 * @file    App_Xmode.c
 * @author  XiaoqiangYuan
 * @version V1.0
 * @date    2018-06-14
 * @brief  Xmode
 *******************************************************************************/
#include "App_Xmode.h"
#include "App_lib.h"

void Func_FeedDog(void)
{
    static uint32_t feeddog_tick = 0;

    if (SysTick_GetTick() < feeddog_tick)
        return;
    feeddog_tick = SysTick_GetTick() + 1000; //每秒喂次狗
    WDT_FeedDog();
}

uint32_t ExFlashStartAdd = 0;

uint16_t Xmdm_Frm_len = C_Xmdm_SOH_Frm_DataSize;
uint16_t Xmdm_Size = C_Xmdm_SOH_Size;

#define dbg(format, ...) printf(format "\n", ##__VA_ARGS__)
void dbg_hex(uint8_t *buf, uint16_t len)
{
    uint8_t cnt = 0;

    while (len--)
    {
        printf("%02X ", *buf);
        buf++;

        if (++cnt == 16)
        {
            printf("\n");
            cnt = 0;
        }
    }
    printf("\n");
}

XmodeRxMsg_Struct XmodeRxMsg;
uint8_t AB_Wt_ExFlashBuff[EXFLASH_PAGE_SIZE];
uint8_t AB_Xmode_RX_Buff[C_Xmdm_STX_Size + 12];

void Xmode_Send(uint8_t data)
{
    UartComm_Send(&data, 1);
}
void Xmode_Send_buff(uint8_t *buff, uint16_t size)
{
    UartComm_Send(buff, size);
}

void Upd_Sta_2_host(uint8_t sta)
{
    XmodeFram_Struct msg = {0};
    msg.Cmd = CHECK_DEV_ACK;
    msg.PackNum = 0;
    msg.PackNumSupp = 0XFF;
    msg.Data[0] = sta;
    uint16_t wcrc = Xmode_Calcrc(&msg.Data, C_Xmdm_SOH_Frm_DataSize);
    msg.Data[C_Xmdm_SOH_Frm_DataSize] = (uint8_t)(wcrc >> 8);
    msg.Data[C_Xmdm_SOH_Frm_DataSize + 1] = (uint8_t)(wcrc);
    Xmode_Send_buff((uint8_t *)&msg.Cmd, C_Xmdm_SOH_Size);
    // dbg_hex((uint8_t *)&msg.Cmd, C_Xmdm_SOH_Size);
}

void AppXmode(void)
{
    XmodeRxMsg.RunSta = C_Xmode_Sta_Start;

    while (XmodeRxMsg.RunSta > C_Xmode_Sta_None)
    {
        Func_FeedDog();
        Xmode_Analytic();
        Xmode_Run_Sta_bp();
    }
}
void Xmode_Run_Sta_bp(void)
{
    switch (XmodeRxMsg.RunSta)
    {
    case C_Xmode_Sta_None:
        break;

    case C_Xmode_Sta_Start:
        dbg("Xmode Start !!!\n\n");
        Xmode_Msg_Init();
        XmodeRxMsg.RunSta = C_Xmode_Sta_CRCX;
        break;

    case C_Xmode_Sta_CRCX:
        Xmode_CRCX_Por();
        XmodeRxMsg.DelayTime = 50;
        XmodeRxMsg.tick = SysTick_GetTick() + XmodeRxMsg.DelayTime;
        break;

    case C_Xmode_Sta_Waite:
        if (XmodeRxMsg.tick > SysTick_GetTick())
            return;

        XmodeRxMsg.tick = SysTick_GetTick() + XmodeRxMsg.DelayTime;
        Xmode_Retry_Por();
        break;

    case C_Xmode_Sta_TmrOver:
        dbg("Xmode TmrOver !!!\n\n");

        XmodeRxMsg.RunSta = C_Xmode_Sta_None;
        break;

    case C_Xmode_Sta_SingFram_RxSucc:
        Xmode_Send(ACK);
        XmodeRxMsg.RetryNum = 0;
        XmodeRxMsg.ChkErrNum = 0;
        XmodeRxMsg.RetryCmd = ACK;
        XmodeRxMsg.RunSta = C_Xmode_Sta_Waite;
        XmodeRxMsg.DelayTime = 500;
        XmodeRxMsg.tick = SysTick_GetTick() + XmodeRxMsg.DelayTime;
        break;

    case C_Xmode_Sta_ChkErr:
        // dbg("Xmode receiving chk err!!!\n\n");
        Xmode_ChkErr_Por();
        XmodeRxMsg.DelayTime = 500;
        XmodeRxMsg.tick = SysTick_GetTick() + XmodeRxMsg.DelayTime;
        break;

    case C_Xmode_Sta_RxFinish:
        dbg("Xmode receiving finish!!!\n\n");
        if (AppCode_Check(XmodeRxMsg.WtExFlashStartAdd, XmodeRxMsg.FirmwareSize))
        {
            Upd_Sta_2_host(C_updsucc);
        }
        else
        {
            Upd_Sta_2_host(C_upd_crc_err);
        }

        XmodeRxMsg.RunSta = C_Xmode_Sta_End;
        break;

    case C_Xmode_Sta_End:
    // dbg("Xmode End!!!\n\n");
    default:
        XmodeRxMsg.RunSta = C_Xmode_Sta_None;
        break;
    }
}

void Xmode_Msg_Init(void)
{
    XmodeRxMsg.RetryNum = 0;
    XmodeRxMsg.ChkErrNum = 0;
    XmodeRxMsg.WtFlashPackNum = 0;
    XmodeRxMsg.WtExFlashPageAdd = 0;

    XmodeRxMsg.WtExFlashStartAdd = OTA_CODE_ADDR;
    XmodeRxMsg.FirmwareSize = APP_FLASH_SIZE;

    XmodeRxMsg.WtExFlashAdd = XmodeRxMsg.WtExFlashStartAdd;
    XmodeRxMsg.ReceiveCodeSize = 0;
}

void Xmode_CRCX_Por(void)
{
    Xmode_Send(CRCX);
    XmodeRxMsg.RetryCmd = CRCX;
    XmodeRxMsg.RunSta = C_Xmode_Sta_Waite;
}

void Xmode_Retry_Por(void)
{
    if (XmodeRxMsg.RetryNum < C_RetryNum_Max)
    {
        // dbg("Happen once Xmode code retry!!\n");
        Xmode_Send(XmodeRxMsg.RetryCmd);
        XmodeRxMsg.RetryNum++;
    }
    else
    {
        XmodeRxMsg.RunSta = C_Xmode_Sta_TmrOver;
    }
}

void Xmode_ChkErr_Por(void)
{
    XmodeRxMsg.ChkErrNum++;

    if (XmodeRxMsg.ChkErrNum < C_ChkErrNum_Max)
    {
        Xmode_Send(NAK);
        XmodeRxMsg.RetryCmd = NAK;
        XmodeRxMsg.RunSta = C_Xmode_Sta_Waite;
    }
    else
    {
        XmodeRxMsg.ChkErrNum = 0;
        XmodeRxMsg.RunSta = C_Xmode_Sta_TmrOver;
    }
}

int Xmode_Calcrc(uint8_t *ptr, int count)
{
    int crc;
    char i;

    crc = 0;

    while (--count >= 0)
    {
        crc = crc ^ (int)*ptr++ << 8;
        i = 8;

        do
        {
            if (crc & 0x8000)
                crc = crc << 1 ^ 0x1021;
            else
                crc = crc << 1;
        } while (--i);
    }

    return (crc);
}
uint8_t Xmode_SOH_check(uint8_t *buf)
{
    uint16_t crc, tcrc;

    crc = (uint16_t)Xmode_Calcrc(buf, C_Xmdm_SOH_Frm_DataSize);
    tcrc = (buf[C_Xmdm_SOH_Frm_DataSize] << 8) + buf[C_Xmdm_SOH_Frm_DataSize + 1];

    if (crc == tcrc)
        return 1;

    dbg("Xmodecheck err crc%04X  tcrc%04X\n\n", crc, tcrc);

    dbg_hex((uint8_t *)&buf, C_Xmdm_SOH_Size);
    return 0;
}

uint8_t Xmode_check(uint8_t *buf)
{
    uint16_t crc, tcrc;

    crc = (uint16_t)Xmode_Calcrc(buf, Xmdm_Frm_len);
    tcrc = (buf[Xmdm_Frm_len] << 8) + buf[Xmdm_Frm_len + 1];

    if (crc == tcrc)
        return 1;

    dbg("Xmodecheck err crc%04X  tcrc%04X\n\n", crc, tcrc);
    dbg_hex((uint8_t *)&buf, Xmdm_Frm_len + 5);
    return 0;
}

uint8_t Xmode_Farm_Analytic(void)
{
    if (XmodeRxMsg.Fram.PackNum != (uint8_t)(~XmodeRxMsg.Fram.PackNumSupp))
    {
        dbg("PackNum %02X  PackNumSupp %02X  \n\n", XmodeRxMsg.Fram.PackNum, XmodeRxMsg.Fram.PackNumSupp);
        return 0;
    }

    if (XmodeRxMsg.Fram.PackNum != (uint8_t)(XmodeRxMsg.WtFlashPackNum + 1))
    {
        dbg("PackNum %02X  WtFlashPackNum %02X\n\n", XmodeRxMsg.Fram.PackNum, XmodeRxMsg.WtFlashPackNum);
        return 0;
    }

    XmodeRxMsg.WtFlashPackNum++;

    memcpy(&AB_Wt_ExFlashBuff[XmodeRxMsg.WtExFlashPageAdd], &XmodeRxMsg.Fram.Data[0], Xmdm_Frm_len);
    XmodeRxMsg.WtExFlashPageAdd += Xmdm_Frm_len;

    if (XmodeRxMsg.WtExFlashPageAdd >= EXFLASH_PAGE_SIZE)
    {
        XmodeRxMsg.WtExFlashPageAdd = 0;
        uint8_t tmp[EXFLASH_PAGE_SIZE];

        Flash_Read(XmodeRxMsg.WtExFlashAdd, tmp, EXFLASH_PAGE_SIZE);
        if (0 == lib_mem_cmp(tmp, AB_Wt_ExFlashBuff, EXFLASH_PAGE_SIZE)) //内容不相同才进行擦写
        {
            // uint32_t *p = AB_Wt_ExFlashBuff;
            // dbg("Flash_Write add:%08X  data:%08X %08X", XmodeRxMsg.WtExFlashAdd, p[0], p[1]);
            Flash_Write(XmodeRxMsg.WtExFlashAdd, AB_Wt_ExFlashBuff, EXFLASH_PAGE_SIZE);
            // Flash_Read(XmodeRxMsg.WtExFlashAdd, tmp, EXFLASH_PAGE_SIZE);
            // p = tmp;
            // dbg("Flash_Read  data:%08X %08X", p[0], p[1]);
            // if (0 == lib_mem_cmp(tmp, AB_Wt_ExFlashBuff, EXFLASH_PAGE_SIZE)) //内容不相同才进行擦写
            // {
            //     dbg("Flash_Write err");
            // }
        }
        XmodeRxMsg.ReceiveCodeSize += EXFLASH_PAGE_SIZE;
        XmodeRxMsg.WtExFlashAdd += EXFLASH_PAGE_SIZE;
    }

    return 1;
}

void Check_DevInfor(void)
{
    err_Sta sta = C_Err_None;
    uint8_t tmp[32 + 1];
    uint8_t appfw[32 + 1] = {0}, apphw[32 + 1] = {0}, applockmode[32 + 1] = {0};
    uint8_t Checkfw[32 + 1] = {0}, Checkhw[32 + 1] = {0}, Checklockmode[32 + 1] = {0};

    Flash_Read(APP_FW_VERSION_INFOR_ADDR, appfw, 32);
    Flash_Read(APP_HW_VERSION_INFOR_ADDR, apphw, 32);
    Flash_Read(DEVICE_TYPE_INFOR_ADDR, applockmode, 32);

    memcpy(Checkfw, &XmodeRxMsg.Fram.Data[0], 32);
    memcpy(Checkhw, &XmodeRxMsg.Fram.Data[32], 32);
    memcpy(Checklockmode, &XmodeRxMsg.Fram.Data[64], 32);
    dbg("[Check_DevInfor]");
    dbg("appfw:%s Checkfw:%s", appfw, Checkfw);
    dbg("apphw:%s Checkhw:%s", apphw, Checkhw);
    dbg("applockmode:%s Checklockmode:%s", applockmode, Checklockmode);
    memset(tmp, 0xff, 32);

    if (lib_mem_cmp(appfw, tmp, 32))
    {
        goto end;
    }

    if (strcmp(applockmode, Checklockmode) != 0)
    {
        sta = C_LockMode_Err;
        goto end;
    }

    if (strcmp(apphw, Checkhw) != 0)
    {
        sta = C_HW_Err;
        goto end;
    }

    if (strcmp(appfw, Checkfw) == 0)
    {
        sta = C_FW_Same;
        goto end;
    }

end:

    if (sta != C_Err_None)
    {
        XmodeRxMsg.RunSta = C_Xmode_Sta_End;
    }

    Upd_Sta_2_host(sta);
}

void Xmode_Analytic(void)
{
    uint16_t len = 0;
    static uint32_t Tick = 0;
    static uint16_t lenback = 0;

    if (SysTick_GetTick() < Tick)
        return;

    Tick = SysTick_GetTick() + 1;

    len = Get_RxBuf_Nun();

    if (len == 0)
        return;

    if (len != lenback)
    {
        lenback = len;
        return;
    }

    lenback = 0;

    len = UartComm_Read_alldata(AB_Xmode_RX_Buff);

    memcpy((uint8_t *)&XmodeRxMsg.Fram, AB_Xmode_RX_Buff, len);

    if (XmodeRxMsg.RunSta < C_Xmode_Sta_Start) // 在此处加这条语句 用于保护当不属于启动状态时不做处理
        return;

    if (XmodeRxMsg.Fram.Cmd == CHECK_DEV)
    {
        Xmdm_Frm_len = C_Xmdm_SOH_Frm_DataSize;

        if (Xmode_check((uint8_t *)&XmodeRxMsg.Fram.Data))
            Check_DevInfor();

        return;
    }

    if (XmodeRxMsg.Fram.Cmd == SOH)
    {
        Xmdm_Frm_len = C_Xmdm_SOH_Frm_DataSize;

        if (Xmode_check((uint8_t *)&XmodeRxMsg.Fram.Data))
        {
            if (Xmode_Farm_Analytic())
            {
                XmodeRxMsg.RunSta = C_Xmode_Sta_SingFram_RxSucc;
                return;
            }
        }
    }
    else if (XmodeRxMsg.Fram.Cmd == STX)
    {
        Xmdm_Frm_len = C_Xmdm_STX_Frm_DataSize;

        if (Xmode_check((uint8_t *)&XmodeRxMsg.Fram.Data))
        {
            if (Xmode_Farm_Analytic())
            {
                XmodeRxMsg.RunSta = C_Xmode_Sta_SingFram_RxSucc;
                return;
            }
        }
    }
    else if (XmodeRxMsg.Fram.Cmd == EOT)
    {
        XmodeRxMsg.RunSta = C_Xmode_Sta_RxFinish;
        return;
    }

    if (len > 128)
        XmodeRxMsg.RunSta = C_Xmode_Sta_ChkErr;
}
